package org.example.erat.service.impl;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.erat.dto.ExperimentDTO;
import org.example.erat.entity.Course;
import org.example.erat.entity.Experiment;
import org.example.erat.exception.ResourceNotFoundException;
import org.example.erat.repository.CourseRepository;
import org.example.erat.repository.ExperimentRepository;
import org.example.erat.service.ExperimentService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 实验服务实现类
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class ExperimentServiceImpl implements ExperimentService {

    private final ExperimentRepository experimentRepository;
    private final CourseRepository courseRepository;

    @Override
    @Transactional
    public ExperimentDTO createExperiment(ExperimentDTO experimentDTO) {
        log.info("创建实验: {}", experimentDTO.getName());

        Course course = courseRepository.findById(experimentDTO.getCourseId())
                .orElseThrow(() -> new ResourceNotFoundException("课程不存在: " + experimentDTO.getCourseId()));

        // 检查实验编号是否已存在
        experimentRepository.findByExperimentNumberAndCourse(experimentDTO.getExperimentNumber(), course)
                .ifPresent(e -> {
                    throw new IllegalArgumentException("该课程下已存在相同编号的实验: " + experimentDTO.getExperimentNumber());
                });

        Experiment experiment = Experiment.builder()
                .name(experimentDTO.getName())
                .experimentNumber(experimentDTO.getExperimentNumber())
                .course(course)
                .build();

        Experiment savedExperiment = experimentRepository.save(experiment);
        return convertToDTO(savedExperiment);
    }

    @Override
    @Transactional
    public ExperimentDTO updateExperiment(Long id, ExperimentDTO experimentDTO) {
        log.info("更新实验: {}", id);

        Experiment experiment = experimentRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("实验不存在: " + id));

        Course course = courseRepository.findById(experimentDTO.getCourseId())
                .orElseThrow(() -> new ResourceNotFoundException("课程不存在: " + experimentDTO.getCourseId()));

        // 检查实验编号是否已被其他实验使用
        experimentRepository.findByExperimentNumberAndCourse(experimentDTO.getExperimentNumber(), course)
                .ifPresent(e -> {
                    if (!e.getId().equals(id)) {
                        throw new IllegalArgumentException("该课程下已存在相同编号的实验: " + experimentDTO.getExperimentNumber());
                    }
                });

        experiment.setName(experimentDTO.getName());
        experiment.setExperimentNumber(experimentDTO.getExperimentNumber());
        experiment.setCourse(course);

        Experiment updatedExperiment = experimentRepository.save(experiment);
        return convertToDTO(updatedExperiment);
    }

    @Override
    @Transactional
    public void deleteExperiment(Long id) {
        log.info("删除实验: {}", id);

        Experiment experiment = experimentRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("实验不存在: " + id));

        experimentRepository.delete(experiment);
    }

    @Override
    public ExperimentDTO getExperiment(Long id) {
        log.info("获取实验: {}", id);

        Experiment experiment = experimentRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("实验不存在: " + id));

        return convertToDTO(experiment);
    }

    @Override
    public List<ExperimentDTO> getAllExperiments() {
        log.info("获取所有实验");

        List<Experiment> experiments = experimentRepository.findAll();
        return experiments.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    @Override
    public List<ExperimentDTO> getExperimentsByCourse(Long courseId) {
        log.info("获取课程的实验列表: {}", courseId);

        Course course = courseRepository.findById(courseId)
                .orElseThrow(() -> new ResourceNotFoundException("课程不存在: " + courseId));

        List<Experiment> experiments = experimentRepository.findByCourse(course);
        return experiments.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional
    public List<ExperimentDTO> batchCreateExperiments(Long courseId, int count) {
        log.info("批量创建实验: 课程ID={}, 数量={}", courseId, count);

        if (count <= 0) {
            throw new IllegalArgumentException("实验数量必须大于0");
        }

        Course course = courseRepository.findById(courseId)
                .orElseThrow(() -> new ResourceNotFoundException("课程不存在: " + courseId));

        // 获取当前最大实验编号
        List<Experiment> existingExperiments = experimentRepository.findByCourse(course);
        int maxExperimentNumber = existingExperiments.stream()
                .mapToInt(Experiment::getExperimentNumber)
                .max()
                .orElse(0);

        // 批量创建实验
        List<Experiment> newExperiments = new ArrayList<>();
        for (int i = 1; i <= count; i++) {
            int experimentNumber = maxExperimentNumber + i;
            Experiment experiment = Experiment.builder()
                    .name("实验" + experimentNumber)
                    .experimentNumber(experimentNumber)
                    .course(course)
                    .build();
            newExperiments.add(experiment);
        }

        List<Experiment> savedExperiments = experimentRepository.saveAll(newExperiments);
        return savedExperiments.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    /**
     * 将实验实体转换为DTO
     * @param experiment 实验实体
     * @return 实验DTO
     */
    private ExperimentDTO convertToDTO(Experiment experiment) {
        return ExperimentDTO.builder()
                .id(experiment.getId())
                .name(experiment.getName())
                .experimentNumber(experiment.getExperimentNumber())
                .courseId(experiment.getCourse().getId())
                .courseName(experiment.getCourse().getName())
                .build();
    }
}
